package com.heo.controller;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.log4j.Logger;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.heo.pojo.CommandResult;
import com.heo.pojo.ControlInfoEntity;
import com.heo.pojo.DeviceInfoEntity;
import com.heo.pojo.DeviceLockInfoEntity;
import com.heo.pojo.OrgInfoEntity;
import com.heo.pojo.req.RebootDevicePara;
import com.heo.pojo.req.StartDevicePara;
import com.heo.pojo.req.UpdateFirmwarePara;
import com.heo.pojo.req.UploadDeviceLogPara;
import com.heo.pub.DictEnum;
import com.heo.service.DeviceInfoService;
import com.heo.service.OrgInfoService;
import com.heo.util.HttpHelper;
import com.heo.util.RedisUtil;

/**
 * @author chichi
 */

@Controller
@Scope("singleton") // 1、为了性能。2、不需要多例。只要controller中不定义属性，那么单例完全是安全的。
@RequestMapping("/")
public class CommandController extends BaseController
{
  private static Logger log = Logger.getLogger(CommandController.class);

  @Resource(name = "orgInfoService")
  private OrgInfoService orgInfoService;

  @Resource(name = "deviceInfoService")
  private DeviceInfoService deviceInfoService;

  /**
   * 启动设备 不同的指令，不同的action入口
   * 
   * @param session
   * @param request
   * @param startDeviceEntity
   * @return
   */
  @ResponseBody
  @RequestMapping(value = "/start.do", method =
  { RequestMethod.POST })
  public JSONObject startDevice(HttpSession session, HttpServletRequest request, @RequestBody StartDevicePara startDevicePara)
  {
    try
    {
      log.debug("[启动设备]收到请求参数：" + startDevicePara.toString());
      String ipAddress = getIpAddr(request);
      // 校验参数
      if (StringUtils.isEmpty(startDevicePara.getDeviceIMEI()))
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.PARA_ERROR, CommandResult.PublicResultMsg.PARA_ERROR);
        return JSON.parseObject(commandResult.toString());
      }
      // 找设备的信息
      DeviceInfoEntity deviceInfo = deviceInfoService.getByKey(startDevicePara.getDeviceIMEI());
      if (deviceInfo == null || deviceInfo.getOrgInfoEntity() == null)
      {
        // 设备不存在。device_info是子表，并且有做关联，所以不可能出现子表有数据，父表没数据的情况。所以如果找不到数据，一定是子表没数据。
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_IS_NOT_EXIST, CommandResult.PublicResultMsg.DEVICE_IS_NOT_EXIST);
        return JSON.parseObject(commandResult.toString());
      }
      // 判断appid是否合法
      if (!(deviceInfo.getOrgInfoEntity().getAppId().equals(startDevicePara.getAppId()) && deviceInfo.getOrgInfoEntity().getAppSecret().equals(startDevicePara.getAppSecret())))
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.AUTHENTICATION_ERROR, CommandResult.PublicResultMsg.AUTHENTICATION_ERROR);
        return JSON.parseObject(commandResult.toString());
      }

      // 判断设备的通道参数是否合法
      if (!StringUtils.isEmpty(startDevicePara.getDeviceChannelIndex()))
      {
        if (startDevicePara.getDeviceChannelIndex() + 1 > deviceInfo.getDeviceChannelNum())
        {
          CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_CHANNEL_INDEX_NOT_EXIST, CommandResult.PublicResultMsg.DEVICE_CHANNEL_INDEX_NOT_EXIST);
          return JSON.parseObject(commandResult.toString());
        }
      }
      // 去redis里找设备的控制信息
      Object redisObj = RedisUtil.getValue(DictEnum.RedisKey.DEVICE_CONTROL + startDevicePara.getAppId() + ":" + startDevicePara.getDeviceIMEI());
      if (redisObj == null)
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_IS_OFFLINE, CommandResult.PublicResultMsg.DEVICE_IS_OFFLINE);
        return JSON.parseObject(commandResult.toString());
      }
      ControlInfoEntity controlInfo = (ControlInfoEntity) JSON.parseObject(redisObj.toString(), ControlInfoEntity.class);
      // 如果设备在redis里的信息不为null，那么就需要查询这个设备的通道的当前的状态
      Byte currStatus = controlInfo.getChannelStatus(startDevicePara.getDeviceChannelIndex());
      if (currStatus == null)
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_CHANNEL_INDEX_NOT_EXIST, CommandResult.PublicResultMsg.DEVICE_CHANNEL_INDEX_NOT_EXIST);
        log.error("[启动设备]启动设备错误，当前设备在redis里的信息不存在。可能已经离线，也可能设备的通道号不存在。");
        return JSON.parseObject(commandResult.toString());
      }
      if (currStatus != DictEnum.DeviceChannelStatus.FREE)
      {
        if (deviceInfo.getAppendCommand() == DictEnum.AppendCommand.DISABLE)
        {
          // 如果设备不允许追加指令，则提示繁忙
          // 如果设备当前的状态不是空闲，那么是不可以再执行其他的指令的。
          String[] statusArray = statusConvertText(currStatus);
          log.error("[启动设备]启动设备错误，当前设备处于非空闲状态。统一返回设备繁忙中。当前设备的状态代码：" + statusArray[0] + "，当前设备的状态：" + statusArray[1]);
          CommandResult commandResult = new CommandResult(CommandResult.QueryDeviceResultCode.BUSY, CommandResult.QueryDeviceResultMsg.BUSY);
          return JSON.parseObject(commandResult.toString());
        }
        else
        {
          String[] statusArray = statusConvertText(currStatus);
          log.debug("[启动设备]启动设备指令已接收，当前设备处于非空闲状态，但设备允许追加指令。当前设备的状态代码：" + statusArray[0] + "，当前设备的状态：" + statusArray[1]);
          //由于允许追加投币，是不同的订单，需要把redis工作状态还原。设置空闲中
          controlInfo.setChannelStatus(startDevicePara.getDeviceChannelIndex(), DictEnum.DeviceChannelStatus.FREE);
          RedisUtil.addValue(DictEnum.RedisKey.DEVICE_CONTROL + startDevicePara.getAppId() + ":" + startDevicePara.getDeviceIMEI(), JSON.toJSONString(controlInfo),
  				180);
          statusArray = statusConvertText(DictEnum.DeviceChannelStatus.FREE);
          log.debug("[启动设备]启动设备指令已接收，当前设备处于非空闲状态，但设备允许追加指令。重新初始化设备状态到redis：" + statusArray[0] + "，当前设备的状态：" + statusArray[1]);
        }
      }
      // 检查设备是否处于锁定状态，如果是锁定状态，则需要看lockTocken是否一致
      // 去redis里查找设备是否处于已经锁定的状态
      // key格式：[LOCK_STATUS]appid:12345678:0
      String redisLockKey = DictEnum.RedisKey.LOCK_STATUS + startDevicePara.getAppId() + ":" + startDevicePara.getDeviceIMEI() + ":" + startDevicePara.getDeviceChannelIndex();
      redisObj = RedisUtil.getValue(redisLockKey);
      // 如果redisObj==null，表示设备没有被锁定。
      if (redisObj != null)
      {
        // 当前设备属于锁定状态，需要检查lockTocken是否合法
        // 检查tocken是否合法
        if (StringUtils.isEmpty(startDevicePara.getLockTocken()))
        {
          CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.LOCK_TOCKEN_IS_NULL, CommandResult.PublicResultMsg.LOCK_TOCKEN_IS_NULL);
          return JSON.parseObject(commandResult.toString());
        }
        // 检查tocken是否和锁定的用户一致
        DeviceLockInfoEntity lockInfo = (DeviceLockInfoEntity) JSON.parseObject(redisObj.toString(), DeviceLockInfoEntity.class);
        if (lockInfo != null && !startDevicePara.getLockTocken().equals(lockInfo.getLockTocken()))
        {
          CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_CHANNEL_IS_LOCKING, CommandResult.PublicResultMsg.DEVICE_CHANNEL_IS_LOCKING);
          return JSON.parseObject(commandResult.toString());
        }
      }

      String url = controlInfo.getControlUrl();
      JSONObject startMsg = new JSONObject();
      startMsg.put("deviceIMEI", startDevicePara.getDeviceIMEI());
      startMsg.put("commandId", startDevicePara.getCommandId());
      startMsg.put("deviceChannelIndex", startDevicePara.getDeviceChannelIndex());
      startMsg.put("startPara", startDevicePara.getStartPara());
      startMsg.put("callbackPara", startDevicePara.getCallbackPara());
      startMsg.put("appId", startDevicePara.getAppId());
      if (StringUtils.isEmpty(startDevicePara.getCallbackExpireTime()) || startDevicePara.getCallbackExpireTime() <= 0)
      {
        startDevicePara.setCallbackExpireTime(24 * 3600);//单位是秒，最长一天
      }
      startMsg.put("callbackExpireTime", startDevicePara.getCallbackExpireTime());
      log.debug("启动设备 >>>>>>>>>>>> url = " + url);
      int result1 = url.indexOf("http://192.168.10.81");
      if(result1 != -1){
    	  url = url.replace("http://192.168.10.81", "http://device-server01.8zao.net");
    	  log.debug("启动设备 修改过后 >>>>>>>>>>>> url = " + url);
      }
      
      String resultJsonStr = HttpHelper.sendHttpPost(url, startMsg.toJSONString());
      //增加数据入redis
      JSONObject param = JSONObject.parseObject(startDevicePara.getCallbackPara());
      if(param != null) {
    	  String orderId = param.getString("orderId");
    	  startMsg.put("url", url);
          startMsg.put("appendCommand", deviceInfo.getAppendCommand());
          log.info("DictEnum.RedisKey.key = " + DictEnum.RedisKey.ORDER + ":" + orderId);
          String orderKey = DictEnum.RedisKey.ORDER + ":" + orderId;
          String orderResendKey = DictEnum.RedisKey.ORDER_RESEND + ":" + orderId;
          //生产环境同时操作两个，时间不一致都会变一致
          RedisUtil.addValue(orderKey, "", 30);
          RedisUtil.addValue(orderResendKey, startMsg.toString(), 45);
      }
      return JSON.parseObject(resultJsonStr);
    }
    catch (Exception e)
    {
      log.error(e.getMessage(), e);
      // 未知错误
      CommandResult pushResult = new CommandResult(CommandResult.PublicResultCode.UNKNOW_ERROR, CommandResult.PublicResultMsg.UNKNOW_ERROR);
      return JSON.parseObject(pushResult.toString());
    }
  }

  /**
   * 重启设备
   * 
   * @param session
   * @param request
   * @param startDevicePara
   * @return
   */
  @ResponseBody
  @RequestMapping(value = "/reboot.do", method =
  { RequestMethod.POST })
  public JSONObject rebootDevice(HttpSession session, HttpServletRequest request, @RequestBody RebootDevicePara rebootDevicePara)
  {
    try
    {
      log.debug("[重启设备]收到请求参数：" + rebootDevicePara.toString());
      String ipAddress = getIpAddr(request);
      // 校验参数
      if (StringUtils.isEmpty(rebootDevicePara.getDeviceIMEI()))
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.PARA_ERROR, CommandResult.PublicResultMsg.PARA_ERROR);
        return JSON.parseObject(commandResult.toString());
      }
      // 找设备的信息
      DeviceInfoEntity deviceInfo = deviceInfoService.getByKey(rebootDevicePara.getDeviceIMEI());
      if (deviceInfo == null || deviceInfo.getOrgInfoEntity() == null)
      {
        // 设备不存在。device_info是子表，并且有做关联，所以不可能出现子表有数据，父表没数据的情况。所以如果找不到数据，一定是子表没数据。
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_IS_NOT_EXIST, CommandResult.PublicResultMsg.DEVICE_IS_NOT_EXIST);
        return JSON.parseObject(commandResult.toString());
      }
      // 判断appid是否合法
      if (!(deviceInfo.getOrgInfoEntity().getAppId().equals(rebootDevicePara.getAppId()) && deviceInfo.getOrgInfoEntity().getAppSecret().equals(rebootDevicePara.getAppSecret())))
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.AUTHENTICATION_ERROR, CommandResult.PublicResultMsg.AUTHENTICATION_ERROR);
        return JSON.parseObject(commandResult.toString());
      }

      // 去redis里找设备的控制信息
      Object redisObj = RedisUtil.getValue(DictEnum.RedisKey.DEVICE_CONTROL + rebootDevicePara.getAppId() + ":" + rebootDevicePara.getDeviceIMEI());
      if (redisObj == null)
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_IS_OFFLINE, CommandResult.PublicResultMsg.DEVICE_IS_OFFLINE);
        return JSON.parseObject(commandResult.toString());
      }
      ControlInfoEntity controlInfo = (ControlInfoEntity) JSON.parseObject(redisObj.toString(), ControlInfoEntity.class);
      String url = controlInfo.getControlUrl();
      JSONObject startMsg = new JSONObject();
      startMsg.put("deviceIMEI", rebootDevicePara.getDeviceIMEI());
      startMsg.put("commandId", rebootDevicePara.getCommandId());
      startMsg.put("appId", rebootDevicePara.getAppId());
      String resultJsonStr = HttpHelper.sendHttpPost(url, startMsg.toJSONString());
      return JSON.parseObject(resultJsonStr);
    }
    catch (Exception e)
    {
      log.error(e.getMessage(), e);
      // 未知错误
      CommandResult pushResult = new CommandResult(CommandResult.PublicResultCode.UNKNOW_ERROR, CommandResult.PublicResultMsg.UNKNOW_ERROR);
      return JSON.parseObject(pushResult.toString());
    }
  }


  /**
   * 上传设备日志
   * 
   * @param session
   * @param request
   * @param queryDevicePara
   * @return
   */
  @ResponseBody
  @RequestMapping(value = "/upload_log.do", method =
  { RequestMethod.POST })
  public JSONObject uploadLogDevice(HttpSession session, HttpServletRequest request, @RequestBody UploadDeviceLogPara uploadDeviceLogPara)
  {
    try
    {
      log.debug("[要求设备上传日志]收到请求参数：" + uploadDeviceLogPara.toString());
      // 校验参数
      if (StringUtils.isEmpty(uploadDeviceLogPara.getDeviceIMEI()))
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.PARA_ERROR, CommandResult.PublicResultMsg.PARA_ERROR);
        return JSON.parseObject(commandResult.toString());
      }
      // 找鉴权信息
      OrgInfoEntity orgInfo = orgInfoService.getByKey(uploadDeviceLogPara.getDeviceIMEI());
      if (orgInfo == null)
      {
        // 设备不存在。device_info是子表，并且有做关联，所以不可能出现子表有数据，父表没数据的情况。所以如果找不到数据，一定是子表没数据。
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_IS_NOT_EXIST, CommandResult.PublicResultMsg.DEVICE_IS_NOT_EXIST);
        return JSON.parseObject(commandResult.toString());
      }
      // 判断appid是否合法
      if (!(orgInfo.getAppId().equals(uploadDeviceLogPara.getAppId()) && orgInfo.getAppSecret().equals(uploadDeviceLogPara.getAppSecret())))
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.AUTHENTICATION_ERROR, CommandResult.PublicResultMsg.AUTHENTICATION_ERROR);
        return JSON.parseObject(commandResult.toString());
      }
      // 去redis里找设备的控制信息
      Object redisObj = RedisUtil.getValue(DictEnum.RedisKey.DEVICE_CONTROL + uploadDeviceLogPara.getAppId() + ":" + uploadDeviceLogPara.getDeviceIMEI());
      if (redisObj == null)
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_IS_OFFLINE, CommandResult.PublicResultMsg.DEVICE_IS_OFFLINE);
        return JSON.parseObject(commandResult.toString());
      }
      // 查询redis里设备的状态。然后返回
      ControlInfoEntity controlInfo = (ControlInfoEntity) JSON.parseObject(redisObj.toString(), ControlInfoEntity.class);
      String url = controlInfo.getControlUrl();
      JSONObject sendMsg = new JSONObject();
      sendMsg.put("deviceIMEI", uploadDeviceLogPara.getDeviceIMEI());
      sendMsg.put("commandId", uploadDeviceLogPara.getCommandId());
      sendMsg.put("endTime", uploadDeviceLogPara.getEndTime());
      if (uploadDeviceLogPara.getLogLength() >= Integer.MAX_VALUE)
      {
        uploadDeviceLogPara.setLogLength(new Integer(Integer.MAX_VALUE));
      }
      sendMsg.put("logLength", uploadDeviceLogPara.getLogLength());
      sendMsg.put("appId", uploadDeviceLogPara.getAppId());
      String resultJsonStr = HttpHelper.sendHttpPost(url, sendMsg.toJSONString());
      return JSON.parseObject(resultJsonStr);
    }
    catch (Exception e)
    {
      log.error(e.getMessage(), e);
      // 未知错误
      CommandResult pushResult = new CommandResult(CommandResult.PublicResultCode.UNKNOW_ERROR, CommandResult.PublicResultCode.UNKNOW_ERROR);
      return JSON.parseObject(pushResult.toString());
    }
  }

  /**
   * 要求设备更新固件
   * 
   * @param session
   * @param request
   * @param updateFirmwarePara
   * @return
   */
  @ResponseBody
  @RequestMapping(value = "/update_firmware.do", method =
  { RequestMethod.POST })
  public JSONObject updateFirmware(HttpSession session, HttpServletRequest request, @RequestBody UpdateFirmwarePara updateFirmwarePara)
  {
    try
    {
      log.debug("[要求设备更新固件]收到请求参数：" + updateFirmwarePara.toString());
      // 校验参数
      if (StringUtils.isEmpty(updateFirmwarePara.getDeviceIMEI()))
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.PARA_ERROR, CommandResult.PublicResultMsg.PARA_ERROR);
        return JSON.parseObject(commandResult.toString());
      }
      // 找鉴权信息
      OrgInfoEntity orgInfo = orgInfoService.getByKey(updateFirmwarePara.getDeviceIMEI());
      if (orgInfo == null)
      {
        // 设备不存在。device_info是子表，并且有做关联，所以不可能出现子表有数据，父表没数据的情况。所以如果找不到数据，一定是子表没数据。
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_IS_NOT_EXIST, CommandResult.PublicResultMsg.DEVICE_IS_NOT_EXIST);
        return JSON.parseObject(commandResult.toString());
      }
      // 判断appid是否合法
      if (!(orgInfo.getAppId().equals(updateFirmwarePara.getAppId()) && orgInfo.getAppSecret().equals(updateFirmwarePara.getAppSecret())))
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.AUTHENTICATION_ERROR, CommandResult.PublicResultMsg.AUTHENTICATION_ERROR);
        return JSON.parseObject(commandResult.toString());
      }
      // 去redis里找设备的控制信息
      Object redisObj = RedisUtil.getValue(DictEnum.RedisKey.DEVICE_CONTROL + updateFirmwarePara.getAppId() + ":" + updateFirmwarePara.getDeviceIMEI());
      if (redisObj == null)
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_IS_OFFLINE, CommandResult.PublicResultMsg.DEVICE_IS_OFFLINE);
        return JSON.parseObject(commandResult.toString());
      }
      // 查询redis里设备的状态。然后返回
      ControlInfoEntity controlInfo = (ControlInfoEntity) JSON.parseObject(redisObj.toString(), ControlInfoEntity.class);
      String url = controlInfo.getControlUrl();
      JSONObject sendMsg = new JSONObject();
      sendMsg.put("deviceIMEI", updateFirmwarePara.getDeviceIMEI());
      sendMsg.put("commandId", updateFirmwarePara.getCommandId());
      sendMsg.put("appId", updateFirmwarePara.getAppId());
      String resultJsonStr = HttpHelper.sendHttpPost(url, sendMsg.toJSONString());
      return JSON.parseObject(resultJsonStr);
    }
    catch (Exception e)
    {
      log.error(e.getMessage(), e);
      // 未知错误
      CommandResult pushResult = new CommandResult(CommandResult.PublicResultCode.UNKNOW_ERROR, CommandResult.PublicResultCode.UNKNOW_ERROR);
      return JSON.parseObject(pushResult.toString());
    }
  }

  /**
   * 首页访问转换
   * 
   * @param request
   * @return
   */
  @RequestMapping(value = "/index.do", produces = "text/plain;charset=UTF-8")
  public String index(HttpServletRequest request)
  {
    return "index.jsp";
  }
}
